package com.franklin.java.service.database;

import com.alibaba.druid.pool.DruidDataSource;
import com.franklin.java.api.classloader.ExternalJarsClassLoader;
import com.franklin.java.entity.CostTime;
import com.franklin.java.entity.sql.JdbcInfo;
import org.apache.commons.lang3.ArrayUtils;

import java.lang.reflect.Proxy;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;
import java.util.function.BiConsumer;

/**
 * Jdbc工具类
 *
 * @author Franklin
 * @since 2021/9/24 15:37
 */
public class JdbcServiceImpl implements JdbcService {

    static JdbcService instance;

    private Map<String, DruidDataSource> driverNameDSMap = new ConcurrentHashMap<>();

    static {
        JdbcService jdbcService = new JdbcServiceImpl();
        ClassLoader classLoader = jdbcService.getClass().getClassLoader();
        Class<?>[] interfaces = jdbcService.getClass().getInterfaces();
        instance = (JdbcService) Proxy.newProxyInstance(
            classLoader,
            interfaces,
            (proxy, method, args) -> {
                if (ArrayUtils.isNotEmpty(args)) {
                    JdbcInfo jdbcInfo = (JdbcInfo) args[0];
                    Thread currentThread = Thread.currentThread();

                    ClassLoader originClassLoader = currentThread.getContextClassLoader();

                    //使用自定义的类加载器
                    currentThread.setContextClassLoader(ExternalJarsClassLoader.getClassLoader(jdbcInfo.getDriverUrl()));

                    Object returnValue = method.invoke(jdbcService, args);

                    //还原回去
                    currentThread.setContextClassLoader(originClassLoader);
                    return returnValue;
                }
                return method.invoke(jdbcService, args);
            }
        );
    }

//    public static JdbcService getInstance(){
//        return instance;
//    }

    public boolean addDataSource(JdbcInfo jdbcInfo) {
        if (driverNameDSMap.containsKey(jdbcInfo.getDriverClassName())) {
            return false;
        }
        //数据源配置
        DruidDataSource dataSource = new DruidDataSource();
        dataSource.setUrl(jdbcInfo.getUrl());
        dataSource.setDriverClassName(jdbcInfo.getDriverClassName()); //这个可以缺省的，会根据url自动识别
        dataSource.setUsername(jdbcInfo.getUsername());
        dataSource.setPassword(jdbcInfo.getPassword());

        //下面都是可选的配置
        dataSource.setInitialSize(1);  //初始连接数，默认0
        dataSource.setMaxActive(1);  //最大连接数，默认8
        dataSource.setMinIdle(1);  //最小闲置数
        dataSource.setMaxWait(2000);  //获取连接的最大等待时间，单位毫秒

        this.driverNameDSMap.put(jdbcInfo.getDriverClassName(), dataSource);
        return true;
    }

    public Connection getConnection(JdbcInfo jdbcInfo) throws SQLException {
        DruidDataSource druidDataSource = this.driverNameDSMap.get(jdbcInfo.getDriverClassName());
        if (Objects.isNull(druidDataSource)) {
            return null;
        }
        return druidDataSource.getConnection();
    }

    @Override
    public CostTime executeQuery(JdbcInfo jdbcInfo, String sql, BiConsumer<ResultSet, CostTime> resultSetConsumer) throws SQLException {
        long start = System.currentTimeMillis();
        Connection connection = getConnection(jdbcInfo);
        Statement statement = connection.createStatement();
        ResultSet resultSet = null;
        long end = start;
        try {
            resultSet = statement.executeQuery(sql);
            end = System.currentTimeMillis();
            CostTime costTime = new CostTime(start, end, TimeUnit.MILLISECONDS);
            resultSetConsumer.accept(resultSet,costTime);
            return costTime;
        } finally {
            if (Objects.nonNull(resultSet)) {
                resultSet.close();
            }
            statement.close();
            connection.close();
        }
    }


}
